介紹完 HPA 水平擴展後,接下來當然就是我們的 VPA 垂直擴展囉!在我個人看來一開始運行一個完全沒有使用過的服務時,是不會清楚知道需要配置多少資源給這項服務的,需要人工長期的觀察調教才能達到理想狀態,如果有個客觀的服務可以給你推薦甚至是自動調整,畫面有點太美,那一定就是 VPA 。有鑑於 VPA 都可以推出兩年有了(?,但 Kubernetes 官方文件中仍然找不到相關的介紹或教學範例,所以以下統整了多方資源再介紹更多有關 VPA 的各種小細節。

在進行之前,我們需要擁有一個已配置 Metrics Server 的 Kubernetes 集群用來收集各種資源指標當作 autoscaling 的依據。
kubectl top node
-------
NAME             CPU(cores)   CPU%   MEMORY(bytes)   MEMORY%   
docker-desktop   258m         6%     5717Mi          72%
如果還沒有安裝的朋友可以參考前面 Metrics Server 篇來安裝。
VPA 運作流程只要由三個主要的元件主成:
Recommender:
requests/limits
Updater:
Pod (因為更新 requests/limits 必須重啟服務)。updateMode: Auto,Recommender 推薦的任何內容都會觸發 Updater 去驅逐 Pod。Admission Controller:
requests/limits 。
從上圖我們可以清楚的理解三個元件彼此互動的模式。
由於官方內建的 API 只有支援 HPA 而已,所以如果我們需要使用到 cluster autoscaler 或者是 vertical autoscaler 等 CRD 時,需要以模組的方式載入。
下載官方 autoscaler repo 並進入 VPA 檔案路徑:
git clone git@github.com:kubernetes/autoscaler.git
cd ./autoscaler/vertical-pod-autoscaler
執行安裝檔:
./hack/vpa-up.sh
-------
customresourcedefinition.apiextensions.k8s.io/verticalpodautoscalercheckpoints.autoscaling.k8s.io created
customresourcedefinition.apiextensions.k8s.io/verticalpodautoscalers.autoscaling.k8s.io created
clusterrole.rbac.authorization.k8s.io/system:metrics-reader created
clusterrole.rbac.authorization.k8s.io/system:vpa-actor created
clusterrole.rbac.authorization.k8s.io/system:vpa-checkpoint-actor created
clusterrole.rbac.authorization.k8s.io/system:evictioner created
clusterrolebinding.rbac.authorization.k8s.io/system:metrics-reader created
clusterrolebinding.rbac.authorization.k8s.io/system:vpa-actor created
clusterrolebinding.rbac.authorization.k8s.io/system:vpa-checkpoint-actor created
clusterrole.rbac.authorization.k8s.io/system:vpa-target-reader created
clusterrolebinding.rbac.authorization.k8s.io/system:vpa-target-reader-binding created
clusterrolebinding.rbac.authorization.k8s.io/system:vpa-evictionter-binding created
serviceaccount/vpa-admission-controller created
clusterrole.rbac.authorization.k8s.io/system:vpa-admission-controller created
clusterrolebinding.rbac.authorization.k8s.io/system:vpa-admission-controller created
clusterrole.rbac.authorization.k8s.io/system:vpa-status-reader created
clusterrolebinding.rbac.authorization.k8s.io/system:vpa-status-reader-binding created
serviceaccount/vpa-updater created
deployment.apps/vpa-updater created
serviceaccount/vpa-recommender created
deployment.apps/vpa-recommender created
Generating certs for the VPA Admission Controller in /tmp/vpa-certs.
Generating RSA private key, 2048 bit long modulus
.+++
..................+++
e is 65537 (0x10001)
unknown option -addext
req [options] <infile >outfile
where options  are
 -inform arg    input format - DER or PEM
 -outform arg   output format - DER or PEM
 -in arg        input file
 -out arg       output file
 -text          text form of request
 -pubkey        output public key
 -noout         do not output REQ
 -verify        verify signature on REQ
 -modulus       RSA modulus
 -nodes         don't encrypt the output key
 -subject       output the request's subject
 -passin        private key password source
 -key file      use the private key contained in file
 -keyform arg   key file format
 -keyout arg    file to send the key to
 -newkey rsa:bits generate a new RSA key of 'bits' in size
 -newkey dsa:file generate a new DSA key, parameters taken from CA in 'file'
 -newkey ec:file generate a new EC key, parameters taken from CA in 'file'
 -[digest]      Digest to sign with (md5, sha1, md4)
 -config file   request template file.
 -subj arg      set or modify request subject
 -multivalue-rdn enable support for multivalued RDNs
 -new           new request.
 -batch         do not ask anything during request generation
 -x509          output a x509 structure instead of a cert. req.
 -days          number of days a certificate generated by -x509 is valid for.
 -set_serial    serial number to use for a certificate generated by -x509.
 -newhdr        output "NEW" in the header lines
 -asn1-kludge   Output the 'request' in a format that is wrong but some CA's
                have been reported as requiring
 -extensions .. specify certificate extension section (override value in config file)
 -reqexts ..    specify request extension section (override value in config file)
 -utf8          input characters are UTF8 (default ASCII)
 -nameopt arg    - various certificate name options
 -reqopt arg    - various request text options
ERROR: Failed to create CA certificate for self-signing. If the error is "unknown option -addext", update your openssl version or deploy VPA from the vpa-release-0.8 branch.
deployment.apps/vpa-admission-controller created
service/vpa-webhook created
這時如果出現了 "unknown option -addext" 代表我們需要提升 openssl 版本。
所以我們需要更新 macOS 預設使用的 libressl (openssl 的一個分支)。
首先卸載剛剛安裝:
./hack/vpa-down.sh
使用 brew 更新 libressl:
brew install libressl
echo 'export PATH="/opt/homebrew/opt/libressl/bin:$PATH"' >> ~/.zshrc
source ~/.zshrc
再次執行安裝檔:
./hack/vpa-up.sh  
------
customresourcedefinition.apiextensions.k8s.io/verticalpodautoscalercheckpoints.autoscaling.k8s.io created
customresourcedefinition.apiextensions.k8s.io/verticalpodautoscalers.autoscaling.k8s.io created
clusterrole.rbac.authorization.k8s.io/system:metrics-reader created
clusterrole.rbac.authorization.k8s.io/system:vpa-actor created
clusterrole.rbac.authorization.k8s.io/system:vpa-checkpoint-actor created
clusterrole.rbac.authorization.k8s.io/system:evictioner created
clusterrolebinding.rbac.authorization.k8s.io/system:metrics-reader created
clusterrolebinding.rbac.authorization.k8s.io/system:vpa-actor created
clusterrolebinding.rbac.authorization.k8s.io/system:vpa-checkpoint-actor created
clusterrole.rbac.authorization.k8s.io/system:vpa-target-reader created
clusterrolebinding.rbac.authorization.k8s.io/system:vpa-target-reader-binding created
clusterrolebinding.rbac.authorization.k8s.io/system:vpa-evictionter-binding created
serviceaccount/vpa-admission-controller created
clusterrole.rbac.authorization.k8s.io/system:vpa-admission-controller created
clusterrolebinding.rbac.authorization.k8s.io/system:vpa-admission-controller created
clusterrole.rbac.authorization.k8s.io/system:vpa-status-reader created
clusterrolebinding.rbac.authorization.k8s.io/system:vpa-status-reader-binding created
serviceaccount/vpa-updater created
deployment.apps/vpa-updater created
serviceaccount/vpa-recommender created
deployment.apps/vpa-recommender created
Generating certs for the VPA Admission Controller in /tmp/vpa-certs.
Generating RSA private key, 2048 bit long modulus
..........+++++
....+++++
e is 010001 (0x65537)
Generating RSA private key, 2048 bit long modulus
..................+++++
............................................................+++++
e is 010001 (0x65537)
Signature ok
subject=/CN=vpa-webhook.kube-system.svc
Getting CA Private Key
Uploading certs to the cluster.
secret/vpa-tls-certs created
Deleting /tmp/vpa-certs.
deployment.apps/vpa-admission-controller created
service/vpa-webhook created
查看 VPA 運行狀況:
kubectl get pods -n kube-system | grep vpa   
-------        
vpa-admission-controller-667dd5b58-jsftm   1/1     Running   0                 84s
vpa-recommender-5f48d76d7-g7x6m            1/1     Running   0                 85s
vpa-updater-6fc5699544-wrvhb               1/1     Running   0                 85s
kubectl api-resources | grep vpa
-------
verticalpodautoscalercheckpoints   vpacheckpoint   autoscaling.k8s.io/v1                  true         VerticalPodAutoscalerCheckpoint
verticalpodautoscalers             vpa             autoscaling.k8s.io/v1                  true         VerticalPodAutoscaler
大功告成!
# deployment.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
  name: hamster
  namespace: default
spec:
  selector:
    matchLabels:
      app: hamster
  replicas: 1
  template:
    metadata:
      labels:
        app: hamster
    spec:
      containers:
        - name: hamster
          image: k8s.gcr.io/ubuntu-slim:0.1
          resources:
            requests:
              cpu: 100m
              memory: 50Mi
            limits:
              cpu: 2000m
              memory: 2Gi
          command: ["/bin/sh"]
          args:
            - "-c"
            - "while true; do timeout 0.2s yes >/dev/null; sleep 0.5s; done"
# vpa.yaml
apiVersion: autoscaling.k8s.io/v1
kind: VerticalPodAutoscaler
metadata:
  name: hamster-vpa
  namespace: default
spec:
  targetRef:
    apiVersion: apps/v1
    kind: Deployment
    name: hamster
  updatePolicy:
    updateMode: "Off"
  resourcePolicy:
    containerPolicies:
      - containerName: '*'
        minAllowed:
          cpu: 100m
          memory: 50Mi
        maxAllowed:
          cpu: 1
          memory: 500Mi
        controlledResources: ["cpu", "memory"]
spec.updatePolic.updateMode :
Off :VPA 只會提供推薦資源配置,不會自動的調整任何設定。Initial :VPA 只會在 Pod 被建立時調整資源配置並且不會再有任何自動調整。Auto :VPA 將會自動配置 Recommender 提供的配置。Recreate :和 Auto 類似差別在於每次重啟 Pod 都會 recreate (很少用到)。spec.resourcePolicy.containerPolicies :
containerName :指定 VPA 的範圍, * 代表目標中所有的 Pod。minAllowed:可調整的資源下限。maxAllowed:可調整的資源上限。controlledResources :需要監控的資源指標,有 cpu 和 memory 可以選擇。執行配置:
kubectl apply -f ./deployment.yaml -f ./vpa.yaml
----------
deployment.apps/hamster created
verticalpodautoscaler.autoscaling.k8s.io/hamster-vpa created
查看 VPA 給出的配置建議:
kubectl get vpa
----------
NAME          MODE   CPU    MEM       PROVIDED   AGE
hamster-vpa   Auto   379m   262144k   True       2m58s
# 262144K 略等於 255 Mi
查看 VPA 的推薦內容:
kubectl describe vpa hamster-vpa
----------
...
Status:
  Conditions:
    Last Transition Time:  2022-08-28T10:03:36Z
    Status:                True
    Type:                  RecommendationProvided
  Recommendation:
    Container Recommendations:
      Container Name:  hamster
      Lower Bound:
        Cpu:     204m
        Memory:  262144k
      Target:
        Cpu:     379m
        Memory:  262144k
      Uncapped Target:
        Cpu:     379m
        Memory:  262144k
      Upper Bound:
        Cpu:     1
        Memory:  500Mi
Events:          <none>
從 Status.Recommendation 中可以看到幾項值得注意的數值:
Lower Bound:如果Pod 的請求小於下限,則Pod 縱向自動擴縮器會刪除該Pod 並將其替換。Upper Bound:如果Pod 的請求大於上限,則Pod 縱向自動擴縮器會刪除該Pod 並將其替換。Target:該值為再 minAllowed maxAllowed 範圍內的推薦值,指定為了使容器以最佳方式運行。Uncapped:不受到 minAllowed maxAllowed 規範的推薦值。./hack/vpa-down.sh
我們可以利用各種 autoscaler 節省許多不必要的浪費,更可以更多的結合 HPA 和 VPA ,但需要注意的是使用非外部資源指標的 HPA 將會與 VPA 的 Auto 模式互相衝突造成不可預期的問題,所以個人比較偏好使用 HPA 搭配 VPA Off 模式,使用推薦值輔助我的資源配置。
千呼萬喚始出來!鐵人賽系列「從異世界歸來發現只剩自己不會 Kubernetes」同名改編作品出版了!
感謝所有交流指教的各路英雄,也感謝願意點閱文章的各位,如果能幫助到任何人都將會是我的榮幸。
本書內容改編自第 14 屆 iThome 鐵人賽 DevOps 組的優選系列文章《從異世界歸來發現只剩自己不會 Kubernetes》。此書是一本綜合性的指南,針對想要探索認識 Kubernetes 的技術人員而生。無論是初涉此領域的新手,還是已有深厚經驗的資深工程師,本書都能提供你所需的知識和技能。
「這本書不僅提供了豐富的範例程式碼和操作指南,讓身為工程師的我們能實際操作來加深認知;更重要的是,它教會我如何從後端工程師的角度去思考和應用 Kubernetes。從容器的生命週期、資源管理到部署管理,每一章都與我們的日常開發工作息息相關。」
──── 雷N │ 後端工程師 / iThome 鐵人賽戰友
天瓏連結: 從異世界歸來發現只剩自己不會 Kubernetes:初心者進入雲端世界的實戰攻略!
相關文章:
相關程式碼同時收錄在:
https://github.com/MikeHsu0618/2022-ithelp/tree/master/Day27
Reference
Vertical Pod Autoscaling: Example | Metrics | Limits | Vertical Pod Autoscaler | VPA | Kubernetes
https://github.com/antonputra/tutorials/blob/main/lessons/074/1-demo/0-deployment.yaml